package uk.co.richardwkirk.parkrun.android.events.displays;

import java.util.LinkedList;
import java.util.List;

import uk.co.richardwkirk.parkrun.android.ActiveSelections;
import uk.co.richardwkirk.parkrun.android.AppSettings;
import uk.co.richardwkirk.parkrun.android.IntentExtras;
import uk.co.richardwkirk.parkrun.android.ParkrunEventData;
import uk.co.richardwkirk.parkrun.android.ParkrunWebDisplay;
import uk.co.richardwkirk.parkrun.android.R;
import uk.co.richardwkirk.parkrun.android.SharedDataReadyActivity;
import uk.co.richardwkirk.parkrun.android.dataload.cache.AthleteCache;
import uk.co.richardwkirk.parkrun.android.news.NewsDisplay;
import uk.co.richardwkirk.parkrun.data.athletes.AthleteSummary;
import uk.co.richardwkirk.parkrun.data.events.Event;
import uk.co.richardwkirk.parkrun.geo.Region;
import uk.co.richardwkirk.parkrun.web.ParkrunURLGenerator;
import android.app.Activity;
import android.content.Intent;
import android.location.Location;
import android.os.Bundle;
import android.util.Log;
import android.view.View;
import android.widget.Toast;

import com.google.android.gms.maps.CameraUpdateFactory;
import com.google.android.gms.maps.GoogleMap;
import com.google.android.gms.maps.MapFragment;
import com.google.android.gms.maps.model.BitmapDescriptorFactory;
import com.google.android.gms.maps.model.LatLng;
import com.google.android.gms.maps.model.Marker;
import com.google.android.gms.maps.model.MarkerOptions;

public class EventDisplay extends SharedDataReadyActivity {

	public static final int DEFAULT_EVENT_ZOOM = 15;
	public static final int DEFAULT_STARTING_ZOOM = 10;
	
	Event selectedEvent = null;
	List<Long> selectedAthletes = null;
	List<AthleteSummary> athleteData = new LinkedList<AthleteSummary>();
	
	// What region are we viewing?
	Region region = null;
	
	LatLng cameraStartLocation = null;
	int startingZoom = DEFAULT_STARTING_ZOOM;
	
	boolean showLocation = false;
	
	public enum EventActionType {
		News,
		Results,
		Web
	}
	
	@Override
	protected void onCreate(Bundle savedInstanceState) {
		super.onCreate(savedInstanceState);
		setContentView(R.layout.event_display);
		
		selectedAthletes = new LinkedList<Long>();
		
		// Make sure we have event data loaded
		loadEventData(IntentExtras.REQUEST_EVENT_DATA_LOAD_ONLY);		
	}

	private void showEvent() {
		boolean isEventSelected = selectedEvent != null;
		if (isEventSelected) {
			this.setTitle(selectedEvent.getFullName());
			createEventMap();
		}
		else {
			this.setTitle("Select Event");
			createRegionMap();
		}
	}

	/** 
	 * Add a map fragment to the event display
	 * @return 
	 * @return 
	 */
	private GoogleMap createMap() {
		GoogleMap map = ((MapFragment) getFragmentManager().findFragmentById(R.id.map)).getMap();
		if (map == null) {
			Toast toast = Toast.makeText(getApplicationContext(), "Could not load map to show events.", AppSettings.WARNING_DISPLAY_TIME);
			toast.show();
		}
		
		// We use a hybrid map type by default
		map.setMapType(GoogleMap.MAP_TYPE_HYBRID);
		
		// Do we want to show the location marker?
		if (showLocation) {
			MarkerOptions markerOptions = new MarkerOptions();
			markerOptions.position(cameraStartLocation);
			markerOptions.title("Current Location");
			markerOptions.icon(BitmapDescriptorFactory.fromResource(R.drawable.current_location));
			map.addMarker(markerOptions);
		}
		
		// Notify what comparison is being done
		notifyComparison();
		
		map.setOnMarkerClickListener(new MarkerClickListener());
		return map;
	}
	
	
	private void notifyComparison() {
		// Do we have a primary selected athlete? That would be the first one.
		AthleteSummary primaryAthlete = (athleteData != null && athleteData.size() > 0 ? athleteData.get(0) : null);
		AthleteSummary compareAthlete = (athleteData != null && athleteData.size() > 1 ? athleteData.get(1) : null);
		
		if (primaryAthlete != null && compareAthlete != null) {
			Toast toast = Toast.makeText(getApplicationContext(), "Comparing " + primaryAthlete.getName() + " and " + compareAthlete.getName(), AppSettings.INFO_DISPLAY_TIME);
			toast.show();
		}
	}


	private class MarkerClickListener implements GoogleMap.OnMarkerClickListener {

		@Override
		public boolean onMarkerClick(Marker marker) {
			selectEvent(marker.getTitle());
			return false;
		}
		
	}
	
	private void selectEvent(String title) {
		selectedEvent = ParkrunEventData.getSingleton().getWorld().getEventByFullName(title);
		showEvent();
	}
	
	/**
	 * Add a map fragment to the event display with the event marked and centred.
	 */
	private void createEventMap() {
		Log.d("PARKRUN", "Creating event map.");
		GoogleMap map = createMap();
		if (selectedEvent != null) {
			// Centre the map on the event
			if (cameraStartLocation == null) {
				cameraStartLocation = new LatLng(selectedEvent.getLocation().getLatitude(), selectedEvent.getLocation().getLongitude());
				startingZoom = DEFAULT_EVENT_ZOOM;
				Log.d("PARKRUN-ZOOM", "Starting zoom set to default: " + startingZoom);
			}
			map.animateCamera(CameraUpdateFactory.newLatLngZoom(cameraStartLocation, startingZoom));
			
			// Marker for the event
			addParkrunMarker(map, selectedEvent);
		}
		map.setOnInfoWindowClickListener(new EventDisplayInfoClickListener());
	}
	
	/**
	 * Add all events in a region to the map.
	 */
	private void createRegionMap() {
		Log.d("PARKRUN", "Creating region map.");
		GoogleMap map = createMap();
		if (region != null) {
			// Centre the map on the event
			if (cameraStartLocation == null) {
				cameraStartLocation = new LatLng(region.getLocation().getLatitude(), region.getLocation().getLongitude());
				startingZoom = region.getZoom();
				Log.d("PARKRUN", "Starting zoom set to region: " + startingZoom + "(" + region.getName() + ")");
			}
			Log.d("PARKRUN", "Moving camera with starting zoom: " + startingZoom);
			map.animateCamera(CameraUpdateFactory.newLatLngZoom(cameraStartLocation, startingZoom));
			
			// Marker for the event
			addAllParkrunMarkers(map, region);
		}
		map.setOnInfoWindowClickListener(new EventDisplayInfoClickListener());
	}

	private void addAllParkrunMarkers(GoogleMap map, Region region) {
		Log.d("PARKRUN", "Adding parkrun markers for region: " + region.getName());
		for (Event event : region.getAllEvents()) {
			addParkrunMarker(map, event);
		}
	}

	
	private void addParkrunMarker(GoogleMap map, Event event) {
		// Add a marker for this parkrun
		LatLng latLng = new LatLng(event.getLocation().getLatitude(), event.getLocation().getLongitude());

		// Do we have a primary selected athlete? That would be the first one.
		AthleteSummary primaryAthlete = (athleteData != null && athleteData.size() > 0 ? athleteData.get(0) : null);
		
		// Who has visited this event?
		List<AthleteSummary> visitors = new LinkedList<AthleteSummary>();
		if (athleteData != null) {
			for (AthleteSummary athlete : athleteData) {
				if (athlete != null && athlete.hasVisited(event.getShortName())) {
					Log.d("PARKRUN", "Event [" + event.getFullName() + "] visited by ["+ athlete.getName() + "]");
					visitors.add(athlete);
				}
			}
		}

		// Create the marker
		MarkerOptions markerOptions = new MarkerOptions();
		markerOptions.position(latLng);
		markerOptions.title(event.getFullName());

		// Add suitable text and image to the marker
		if (visitors.size() == 0) {
			markerOptions.icon(BitmapDescriptorFactory.fromResource(R.drawable.parkrun_balloon));
		}
		else if (visitors.size() == 1 && visitors.get(0) == primaryAthlete) {
			markerOptions.icon(BitmapDescriptorFactory.fromResource(R.drawable.parkrun_visited_primary_balloon));
		}
		else if (visitors.size() == 1) {
			markerOptions.icon(BitmapDescriptorFactory.fromResource(R.drawable.parkrun_visited_other_balloon));
		}
		else {
			markerOptions.icon(BitmapDescriptorFactory.fromResource(R.drawable.parkrun_visited_both_balloon));
		}
		
		StringBuilder snippet = new StringBuilder();
		for (AthleteSummary athlete : visitors) {
			snippet.append(athlete.getName()).append(": ");
			snippet.append(athlete.getEventText(event.getShortName()));
			snippet.append("\n");
		}
		if (snippet.length() > 0) {
			markerOptions.snippet(snippet.toString());
		}
		
		// Show the marker
		map.addMarker(markerOptions);
		
	}
	

	private class EventDisplayInfoClickListener implements GoogleMap.OnInfoWindowClickListener {

		@Override
		public void onInfoWindowClick(Marker arg0) {
			doAction(EventActionType.Web);
		}
		
	}
	
	/**
	 * Load the parkrun news for this event.
	 * @param view
	 */
	public void showNews(View view) {
		doAction(EventActionType.News);
	}
	
	
	/**
	 * Load the parkrun news for this event.
	 * @param view
	 */
	public void showResults(View view) {
		doAction(EventActionType.Results);
	}
	
	/**
	 * Load the parkrun event website for this event.
	 * @param view
	 */
	public void showWebsite(View view) {
		doAction(EventActionType.Web);
	}


	// TODO: Can we get this called direct from the onClick?
	private void doAction(EventActionType type) {
		
		// We can only do an action if an event has been selected
		if (selectedEvent == null) {
			Toast toast = Toast.makeText(getApplicationContext(), "No event selected.", AppSettings.WARNING_DISPLAY_TIME);
			toast.show();
			return;
		}
		
		// Do the action
		Intent intent = null;
		switch (type) {
			case News:
				intent = new Intent(this, NewsDisplay.class);
				intent.putExtra(IntentExtras.EXTRA_NEWS_TYPE, IntentExtras.EXTRA_NEWS_TYPE_EVENT);
				intent.putExtra(IntentExtras.EXTRA_SELECTED_EVENT, selectedEvent.getShortName());
				startActivity(intent);
				break;
			case Results:
				intent = new Intent(this, ParkrunWebDisplay.class);
				intent.putExtra(IntentExtras.EXTRA_PARKRUN_WEBPAGE_URL, selectedEvent.getEventURL(ParkrunURLGenerator.ParkrunWebPages.LatestResults));
				intent.putExtra(IntentExtras.EXTRA_PARKRUN_WEBPAGE_TITLE, selectedEvent.getFullName() + " Results");
				startActivity(intent);
				break;
			case Web:
				intent = new Intent(this, ParkrunWebDisplay.class);
				intent.putExtra(IntentExtras.EXTRA_PARKRUN_WEBPAGE_URL, selectedEvent.getEventURL(ParkrunURLGenerator.ParkrunWebPages.Home));
				intent.putExtra(IntentExtras.EXTRA_PARKRUN_WEBPAGE_TITLE, selectedEvent.getFullName());
				startActivity(intent);
				break;
			default:
				break;
		}
	}

	/**
	 * Static methods to start this display
	 */
	public static void startEventDisplay(Activity parentActivity, Event selectedEvent) {
		Intent intent = new Intent(parentActivity, EventDisplay.class);
		intent.putExtra(IntentExtras.EXTRA_SELECTED_EVENT, selectedEvent.getShortName());
		parentActivity.startActivity(intent);
	}
	
	public static void startRegionDisplay(Activity parentActivity, Region selectedRegion) {
		Intent intent = new Intent(parentActivity, EventDisplay.class);
		intent.putExtra(IntentExtras.EXTRA_SELECTED_REGION, selectedRegion.getId());
		parentActivity.startActivity(intent);
	}

	public static void startRegionDisplay(Activity parentActivity, Region selectedRegion, Location location) {
		Intent intent = new Intent(parentActivity, EventDisplay.class);
		intent.putExtra(IntentExtras.EXTRA_SELECTED_REGION, selectedRegion.getId());
		intent.putExtra(IntentExtras.EXTRA_LOCATION_LATITUDE, location.getLatitude());
		intent.putExtra(IntentExtras.EXTRA_LOCATION_LONGITUDE, location.getLongitude());
		parentActivity.startActivity(intent);
	}
	
	public static void startRegionDisplay(Activity parentActivity, Region selectedRegion, long athleteNumber, long compareAthlete) {
		Log.i("PARKRUN", "Start region display to compare [" + athleteNumber + "] and [" + compareAthlete + "]");
		Intent intent = new Intent(parentActivity, EventDisplay.class);
		intent.putExtra(IntentExtras.EXTRA_COMPARE_ATHLETE, compareAthlete);
		intent.putExtra(IntentExtras.EXTRA_SELECTED_REGION, selectedRegion.getId());
		parentActivity.startActivity(intent);
	}
	

	@Override
	protected void handleEventLoadResponse(int requestCode) {
		// When we know we have event data, we can show the event
		Intent intent = getIntent();
		String eventShortName = intent.getStringExtra(IntentExtras.EXTRA_SELECTED_EVENT);
		if (eventShortName != null) {
			selectedEvent = ParkrunEventData.getSingleton().getWorld().getEvent(eventShortName);
		}

		int regionId = intent.getIntExtra(IntentExtras.EXTRA_SELECTED_REGION, 1);
		region = ParkrunEventData.getSingleton().getWorld();
		if (regionId > 1) {
			region = region.getRegion(regionId);
		}
		
		double locationLat = intent.getDoubleExtra(IntentExtras.EXTRA_LOCATION_LATITUDE, 0.0);
		double locationLong = intent.getDoubleExtra(IntentExtras.EXTRA_LOCATION_LONGITUDE, 0.0);
		if (locationLat != 0.0 || locationLong != 0.0) {
			// Set the location if one has been provided. You are in trouble if you are at 0 long & lat.
			cameraStartLocation = new LatLng(locationLat, locationLong);
			showLocation = true;
		}
		else {
			// If no lat and long specified, show the default country
			String country = getDefaultCountry();
			Region myCountry = ParkrunEventData.getSingleton().getWorld().getRegion(country);
			if (myCountry != null) {
				Log.d("PARKRUN", "Setting starting camera location to " + myCountry.getName());
				cameraStartLocation = new LatLng(myCountry.getLocation().getLatitude(), myCountry.getLocation().getLongitude());
				startingZoom = myCountry.getZoom();
			}
		}
		
		// Either load athlete data or show the event without athlete details
		long selectedAthlete = ActiveSelections.getSingleton().getAthleteId();
		Log.i("PARKRUN", "Selected athlete = " + selectedAthlete);
		if (selectedAthlete > 0) {
			selectedAthletes.add(selectedAthlete);
		}
		
		long compareAthlete = intent.getLongExtra(IntentExtras.EXTRA_COMPARE_ATHLETE, 0);
		Log.i("PARKRUN", "Compare athlete = " + compareAthlete);
		if (compareAthlete > 0) {
			selectedAthletes.add(compareAthlete);
		}
		
		if (selectedAthletes.size() > 0) {
			loadAthleteData(IntentExtras.REQUEST_ATHLETE_DATA_LOAD, selectedAthletes, false);
		}
		else {
			showEvent();
		}
	}

	@Override
	protected void handleCountrySelectionResult(int requestCode, Intent data) {
		// TODO: Do we want to move to the country location?
	}

	@Override
	protected void handleAthleteLoadResponse(int requestCode) {
		// Show the event with an athlete's details included
		for (Long athleteNumber : selectedAthletes) {
			AthleteSummary athlete = AthleteCache.getSingleton().getItem(String.valueOf(athleteNumber));
			athleteData.add(athlete);
		}
		showEvent();
	}

	@Override
	protected void handleDataLoadError(int requestCode) {
		finish();
	}

	@Override
	protected void handleDataLoadCancel(int requestCode) {
		finish();
	}	
	
}
